﻿#include "pch.h"
#include "sbhs.h"

//////////////////////////////////////////////////////////////////////////
// 만들기

// 만들기
kBase* k_crbs_new(kType type)
{
	kBase* self;
	kBase* pass;
	kTypeInfo* info;

	info = k_tmnt_look_type(type);

	if (!info)
	{
		k_traces(KLV_FATAL, "k.crbs", "cannot find type information!");
		return NULL;
	}

	if (info->flags & KTIF_ABSTRACT)
	{
		k_traces(KLV_CRITICAL, "k.crbs", "cannot create abstract base!");
		return NULL;
	}

	pass = (kBase*)k_new_zero(info->size, kbyte);

	// 이렇게하는 이유는 kBase를 받은 C++가 abstract일 경우 포인터 위치가 vtbl 만큼 이동하기 때문
	self = info->func_cnst(pass);

	if (!self)
	{
		// disp 호출 안함!!!
		k_delete(pass);
	}
	else
	{
		self->type_info = info;
		self->base_ref = 1;

		k_atominc_ptr(&info->refs);
		k_atominc_ptr(&info->accs);
	}

	return self;
}

// 만들기
kBase* k_crbs_check(kType type, kBase* self)
{
	kTypeInfo* info;

	info = k_tmnt_look_type(type);

	if (!info)
	{
		k_traces(KLV_FATAL, "k.crbs", "cannot find type information!");
		return NULL;
	}

	if (info->flags & KTIF_ABSTRACT)
	{
		k_traces(KLV_CRITICAL, "k.crbs", "cannot create abstract base!");
		return NULL;
	}

	self->type_info = info;
	self->base_ref = 1;

	k_atominc_ptr(&info->refs);
	k_atominc_ptr(&info->accs);

	return self;
}


//////////////////////////////////////////////////////////////////////////
// 베이스

//
static void _k_base_type_init(kType type);
static kBase* _k_base_do_cnst(kpointer ptr);
static kpointer _k_base_do_disp(kBase* ptr);

// 초기화
void _k_type_base_init(void)
{
	// 기본
	static kTypeDecl decl_base =
	{
		sizeof(kBase), KTIF_REF | KTIF_ABSTRACT,
		_k_base_type_init, NULL, _k_base_do_cnst, _k_base_do_disp
	};
	// 서술
	static kTypeDecl decl_desc =
	{
		sizeof(kDescBase), KTIF_REF | KTIF_ABSTRACT,
		NULL, NULL, NULL, NULL
	};
	// 노드
	static kTypeDecl decl_node =
	{
		sizeof(kNodeBase), KTIF_REF | KTIF_ABSTRACT,
		NULL, NULL, NULL, NULL
	};

	k_tmnt_reg_red((kint)KTYPE_BASE, "Base", &decl_base, KTYPE_NULL);
	k_tmnt_reg_red((kint)KTYPE_DESC, "DescBase", &decl_desc, KTYPE_BASE);
	k_tmnt_reg_red((kint)KTYPE_NODE, "NodeBase", &decl_node, KTYPE_BASE);
}

// 타입
kType k_base_type(void)
{
	return KTYPE_BASE;
}

// 타입 초기화
static void _k_base_type_init(kType type)
{
	k_type_set_roam(type, K_MAX_USHORT);
}

// 생성
static kBase* _k_base_do_cnst(kpointer ptr)
{
	return (kBase*)ptr;
}

// 제거
static kpointer _k_base_do_disp(kBase* ptr)
{
	return (kpointer)ptr;
}

// 로드
kBase* k_base_load(kBase* self)
{
	k_atominc(&self->base_ref);

	return self;
}

// 언로드
kBase* k_base_unload(kBase* self)
{
	int ref = k_atomdec(&self->base_ref);

	if (ref != 0)
	{
		k_assert(ref > 0, "invalid reference count!");
		return self;
	}
	else
	{
		k_base_force_unload(self);
		return NULL;
	}
}

// 참조 로드
int k_base_ref_load(kBase* self)
{
	return k_atominc(&self->base_ref);
}

// 참조 언로드
int k_base_ref_unload(kBase* self)
{
	int ref = k_atomdec(&self->base_ref);

	if (ref != 0)
	{
		k_assert(ref > 0, "invalid reference count!");
		return ref;
	}
	else
	{
		k_base_force_unload(self);
		return 0;
	}
}

// 순수 언로드
void k_base_pure_unload(kBase* ptr)
{
	if (ptr)
		k_base_unload(ptr);
}

// 순수 로드
void k_base_pure_load(kBase* ptr)
{
	if (ptr)
		k_base_load(ptr);
}

// 포스 언로드
void k_base_force_unload(kBase* self)
{
	kTypeInfo* info;
	kBase* pass;

	info = self->type_info;

	// 만들 때도 마찬가지로 C++ virtual 테이블에 의한 밀림 때문에
	pass = info->func_disp(self);

	if (!pass)
	{
		// 지우지 말라는 신의 계신가 보다
		self->base_ref = -20131118;		// 마지막으로 소스 파일을 확인한 날짜

		k_atomdec_ptr(&info->refs);
	}
	else
	{
		// 지움
#ifdef _SB_DEBUG_
		if (!k_memtest(pass))
			k_tracef(KLV_FATAL, "k.base", "invalid memory ptr [0x%p]", pass);
#endif

		if (self->base_ref == 0)
			k_atomdec_ptr(&info->refs);

		k_delete(pass);
	}
}

// 살림
void k_base_force_revive(kBase* self)
{
	kTypeInfo* info = self->type_info;

	self->base_ref = 1;

	k_atominc_ptr(&info->refs);
}

// 참조
int k_base_get_ref(kBase* ptr)
{
	kBase* self = (kBase*)ptr;

	return self->base_ref;
}

// 타입
kType k_base_get_type(kBase* ptr)
{
	kBase* self = (kBase*)ptr;

	return self->type_info->supers[0];
}

// 데이터
kpointer k_base_get_userdata(kBase* ptr)
{
	kBase* self = (kBase*)ptr;

	return self->base_data;
}

// 데이터
kpointer k_base_set_userdata(kBase* ptr, kpointer data)
{
	kBase* self = (kBase*)ptr;
	kpointer prev = self->base_data;

	self->base_data = data;

	return prev;
}


//////////////////////////////////////////////////////////////////////////
// 서술자

//
kType k_descbase_type(void)
{
	return KTYPE_DESC;
}

//
kuintptr k_descbase_get_desc(kDescBase* self)
{
	return self->hd;
}

//
kuintptr k_descbase_set_desc(kDescBase* self, kuintptr hd)
{
	kuintptr prev = self->hd;

	self->hd = hd;

	return prev;
}


//////////////////////////////////////////////////////////////////////////
// 노드

//
kType k_nodebase_type(void)
{
	return KTYPE_NODE;
}

//
ksize_t k_nodebase_get_hash_code(kNodeBase* self)
{
	return self->hash;
}


//////////////////////////////////////////////////////////////////////////
// 목록
//

//
void k_baselist_init(kBaseList* p)
{
	p->frst = p->last = NULL;
	p->count = 0;
}
//
void k_baselist_disp(kBaseList* p)
{
	k_baselist_clear(p);
}

//
void k_baselist_clear(kBaseList* p)
{
	kNodeBase* node;
	kNodeBase* next;

	for (node = p->frst; node; node = next)
	{
		next = node->next;

		if (k_base_ref_unload((kBase*)node) != 0)
		{
			node->next = NULL;
			node->prev = NULL;
		}
	}

	p->frst = p->last = NULL;
	p->count = 0;
}

//
void k_baselist_clear_chain(kBaseList* p)
{
	kNodeBase* node;

	for (;;)
	{
		node = p->frst;

		if (!node)
			break;

		k_base_force_unload((kBase*)node);
	}

	p->frst = p->last = NULL;
	p->count = 0;
}


//
void k_baselist_clear_force(kBaseList* p)
{
	kNodeBase* node;
	kNodeBase* next;

	for (node = p->frst; node; node = next)
	{
		next = node->next;
		k_base_force_unload((kBase*)node);
	}

	p->frst = p->last = NULL;
	p->count = 0;
}

void k_baselist_append(kBaseList* p, kNodeBase* node)
{
	if (p->frst)
		p->frst->prev = node;
	else
		p->last = node;

	node->next = p->frst;
	node->prev = NULL;
	p->frst = node;
	p->count++;
}

void k_baselist_prepend(kBaseList* p, kNodeBase* node)
{
	if (p->last)
		p->last->next = node;
	else
		p->frst = node;

	node->prev = p->last;
	node->next = NULL;
	p->last = node;
	p->count++;
}

kNodeBase* k_baselist_remove(kBaseList* p, kNodeBase* node)
{
	if (node->next)
		node->next->prev = node->prev;
	else
		p->last = node->prev;

	if (node->prev)
		node->prev->next = node->next;
	else
		p->frst = node->next;

	p->count--;

	return node;
}

kNodeBase* k_baselist_remove_first(kBaseList* p)
{
	kNodeBase* node = p->last;
	return k_baselist_remove(p, node);
}

kNodeBase* k_baselist_remove_last(kBaseList* p)
{
	kNodeBase* node = p->frst;
	return k_baselist_remove(p, node);
}

kcham k_baselist_contains(kBaseList* p, kNodeBase* item)
{
	kNodeBase* node;

	for (node = p->frst; node; node = node->next)
	{
		if (node == item)
			return TRUE;
	}

	return FALSE;
}

void k_baselist_foreach(kBaseList* p, void(*func2)(kpointer, kNodeBase*), kpointer userdata)
{
	kNodeBase* node;

	for (node = p->last; node; node = node->prev)
		func2(userdata, node);
}

void k_baselist_loopeach(kBaseList* p, void(*func1)(kNodeBase*))
{
	kNodeBase* node;

	for (node = p->last; node; node = node->prev)
		func1(node);
}


//////////////////////////////////////////////////////////////////////////
// 묶음

static void _k_basemukum_pure_resize(kBaseMukum* p);
static void _k_basemukum_pure_test(kBaseMukum* p);
static kNodeBase** _k_basemukum_pure_lookup_base(kBaseMukum* p, kNodeBase* key, ksize_t* rethash);
static kNodeBase** _k_basemukum_pure_lookup_key(kBaseMukum* p, kconstpointer key, ksize_t* rethash);
static kNodeBase* _k_basemukum_pure_erase(kBaseMukum* p, kconstpointer key);
static kcham _k_basemukum_pure_erase_base(kBaseMukum* p, kNodeBase* base);
static void _k_basemukum_pure_erase_node(kBaseMukum* p, kNodeBase*** ppnode);
static void _k_basemukum_pure_erase_chain(kBaseMukum* p);
static void _k_basemukum_pure_erase_all(kBaseMukum* p, kcham isforce);
static kcham _k_basemukum_pure_add(kBaseMukum* p, kNodeBase* node);

kcham k_basemukum_init(kBaseMukum* p, kType type)
{
	p->typeinfo = k_type_get_info(type);
	k_return_value_if_fail(p->typeinfo != NULL, FALSE);
	k_return_value_if_fail(p->typeinfo->func_hash != NULL, FALSE);
	k_return_value_if_fail(p->typeinfo->func_eq != NULL, FALSE);
	k_return_value_if_fail(p->typeinfo->func_key != NULL, FALSE);

	p->revision = 0;
	p->count = 0;
	p->bucket = K_MIN_HASH;
	p->nodes = k_new_zero(K_MIN_HASH, kNodeBase*);

	return TRUE;
}

void k_basemukum_disp(kBaseMukum* p)
{
	k_basemukum_clear(p);

	k_delete(p->nodes);
}

void k_basemukum_clear(kBaseMukum* p)
{
	_k_basemukum_pure_erase_all(p, FALSE);
	_k_basemukum_pure_test(p);
}

void k_basemukum_clear_chain(kBaseMukum* p)
{
	_k_basemukum_pure_erase_chain(p);
	_k_basemukum_pure_test(p);
}

void k_basemukum_clear_force(kBaseMukum* p)
{
	_k_basemukum_pure_erase_all(p, TRUE);
	_k_basemukum_pure_test(p);
}

kcham k_basemukum_add(kBaseMukum* p, kNodeBase* base)
{
	if (!_k_basemukum_pure_add(p, base))
		return FALSE;
	else
	{
		_k_basemukum_pure_test(p);
		return TRUE;
	}
}

kNodeBase* k_basemukum_get(kBaseMukum* p, kconstpointer key)
{
	kNodeBase** gnode = _k_basemukum_pure_lookup_key(p, key, NULL);
	return *gnode;
}

kNodeBase* k_basemukum_remove(kBaseMukum* p, kconstpointer key)
{
	kNodeBase* node = _k_basemukum_pure_erase(p, key);

	if (!node)
		return NULL;
	else
	{
		_k_basemukum_pure_test(p);
		return node;
	}
}

kNodeBase* k_basemukum_remove_base(kBaseMukum* p, kNodeBase* base)
{
	if (!_k_basemukum_pure_erase_base(p, base))
		return NULL;
	else
	{
		_k_basemukum_pure_test(p);
		return base;
	}
}
void k_basemukum_foreach(kBaseMukum* p, void(*func2)(kpointer, kNodeBase*), kpointer userdata)
{
	kNodeBase* node;
	ksize_t i;

	for (i = 0; i < p->bucket; i++)
	for (node = p->nodes[i]; node; node = node->sib)
		func2(userdata, node);
}

void k_basemukum_loopeach(kBaseMukum* p, void(*func1)(kNodeBase*))
{
	kNodeBase* node;
	ksize_t i;

	for (i = 0; i < p->bucket; i++)
	for (node = p->nodes[i]; node; node = node->sib)
		func1(node);
}
static void _k_basemukum_pure_resize(kBaseMukum* p)
{
	kNodeBase** newnodes;
	kNodeBase* node;
	kNodeBase* sib;
	ksize_t hashmask;
	ksize_t newbucket, i;

	newbucket = k_primeclose(p->count);
	newbucket = K_CLAMP(newbucket, K_MIN_HASH, K_MAX_HASH);
	newnodes = k_new_zero(newbucket, kNodeBase*);

	for (i = 0; i < p->bucket; i++)
	{
		for (node = p->nodes[i]; node; node = sib)
		{
			sib = node->sib;
			hashmask = node->hash % newbucket;
			node->sib = newnodes[hashmask];
			newnodes[hashmask] = node;
		}
	}

	k_delete(p->nodes);
	p->nodes = newnodes;
	p->bucket = newbucket;
}

static void _k_basemukum_pure_test(kBaseMukum* p)
{
	ksize_t cnt = p->count;
	ksize_t bk = p->bucket;

	if ((bk >= 3 * cnt && bk > K_MIN_HASH) ||
		(3 * bk <= cnt && bk < K_MAX_HASH))
		_k_basemukum_pure_resize(p);
}

static kNodeBase** _k_basemukum_pure_lookup_base(kBaseMukum* p, kNodeBase* base, ksize_t* rethash)
{
	kNodeBase** lnode;
	kNodeBase* node;
	kconstpointer key;
	ksize_t hash;

	key = p->typeinfo->func_key(base);
	hash = p->typeinfo->func_hash(key);
	lnode = &p->nodes[hash % p->bucket];

	while ((node = *lnode) != NULL)
	{
		if (node->hash == hash && p->typeinfo->func_eq(node, key))
			break;

		lnode = &node->sib;
	}

	if (rethash)
		*rethash = hash;

	return lnode;
}

static kNodeBase** _k_basemukum_pure_lookup_key(kBaseMukum* p, kconstpointer key, ksize_t* rethash)
{
	kNodeBase** lnode;
	kNodeBase* node;
	ksize_t hash;

	hash = p->typeinfo->func_hash(key);
	lnode = &p->nodes[hash % p->bucket];

	while ((node = *lnode) != NULL)
	{
		if (node->hash == hash && p->typeinfo->func_eq(node, key))
			break;

		lnode = &node->sib;
	}

	if (rethash)
		*rethash = hash;

	return lnode;
}

static void _k_basemukum_pure_erase_chain(kBaseMukum* p)
{
	ksize_t i;

	for (i = 0; i < p->bucket; i++)
	{
		for (;;)
		{
			kNodeBase* node = p->nodes[i];

			if (!node)
				break;

			k_base_force_unload((kBase*)node);
		}
	}

	p->count = 0;
	p->revision++;
	memset(p->nodes, 0, p->bucket*sizeof(kNodeBase*));
}

static void _k_basemukum_pure_erase_all(kBaseMukum* p, kcham isforce)
{
	ksize_t i;
	kNodeBase* node;
	kNodeBase* next;

	if (isforce)
	{
		for (i = 0; i < p->count; i++)
		{
			for (node = p->nodes[i]; node; node = next)
			{
				next = node->sib;
				k_base_force_unload((kBase*)node);
			}
		}
	}
	else
	{
		for (i = 0; i < p->count; i++)
		{
			for (node = p->nodes[i]; node; node = next)
			{
				next = node->sib;

				if (k_base_ref_unload((kBase*)node) > 0)
					node->sib = NULL;
			}
		}
	}

	p->count = 0;
	p->revision++;
	memset(p->nodes, 0, p->bucket * sizeof(kNodeBase*));
}

static kcham _k_basemukum_pure_erase_base(kBaseMukum* p, kNodeBase* base)
{
	kNodeBase** rnode;
	kNodeBase* node;
	ksize_t hash;

	k_return_value_if_fail(base != NULL, FALSE);

	hash = base->hash;
	rnode = &p->nodes[hash % p->bucket];

	while ((node = *rnode) != NULL)
	{
		if (node == base)
		{
			_k_basemukum_pure_erase_node(p, &rnode);
			return TRUE;
		}

		rnode = &node->sib;
	}

	return FALSE;
}

static void _k_basemukum_pure_erase_node(kBaseMukum* p, kNodeBase** * ppnode)
{
	kNodeBase** nnode = *ppnode;
	kNodeBase* node = *nnode;

	*nnode = node->sib;

	p->count--;
	p->revision++;
}

static kNodeBase* _k_basemukum_pure_erase(kBaseMukum* p, kconstpointer key)
{
	kNodeBase** rnode;
	kNodeBase* node;

	rnode = _k_basemukum_pure_lookup_key(p, key, NULL);
	node = *rnode;

	if (!node)
		return NULL;
	else
	{
		_k_basemukum_pure_erase_node(p, &rnode);
		return node;
	}
}

static kcham _k_basemukum_pure_add(kBaseMukum* p, kNodeBase* node)
{
	ksize_t hash;
	kNodeBase** anode;

	anode = _k_basemukum_pure_lookup_base(p, node, &hash);

	if (*anode)
		return FALSE;
	else
	{
		node->sib = NULL;
		node->hash = hash;
		*anode = node;

		p->revision++;
		p->count++;

		return TRUE;
	}
}

